package com.ho1ho.audioexample;

import android.content.Intent;
import android.media.AudioFormat;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.View;
import android.widget.EditText;

import androidx.appcompat.app.AppCompatActivity;

import com.ho1ho.audioexample.others.Callback;
import com.ho1ho.audioexample.tcp.TcpClient;
import com.ho1ho.audioexample.tcp.TcpServer;
import com.ho1ho.audioexample.utils.AudioPlay;
import com.ho1ho.audioexample.utils.MicRecord;
import com.ho1ho.audioexample.utils.others.PcmToWavUtil;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    // Audio sample rate. The standard value is 44100 for all devices.
    // However, some devices are still support the following sample rates like: 11025, 16000, 22050 and etc.
    private static final int[] SUPPORT_SAMPLE_RATES = {8000, 11025, 16000, 22050, 44100, 48000};
    // Bitrate. Kilobits per second.
    private static final int[] SUPPORT_BITRATES = {32000, 64000, 96000, 128000, 192000, 256000};

    public static final int DEFAULT_SAMPLE_RATES = SUPPORT_SAMPLE_RATES[2];
    public static final int DEFAULT_BIT_RATES = SUPPORT_BITRATES[0];
    public static final int DEFAULT_CHANNEL_COUNT = 1;
    public static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

    public static final int CHANNEL_IN = AudioFormat.CHANNEL_IN_MONO;
    public static final int CHANNEL_OUT = AudioFormat.CHANNEL_OUT_MONO;
    // 0x12, 0x08 = 44.1Khz, mono
    // 0x14, 0x08 = 16Khz, mono
    public static final byte[] AUDIO_CSD_0 = new byte[]{(byte) 0x14, (byte) 0x08};

    private MicRecord mMicRecord;
    private AudioPlay mAudioPlay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e(TAG, "CacheDir        =" + getCacheDir().getAbsoluteFile());
        Log.e(TAG, "ExternalCacheDir=" + getExternalCacheDir().getAbsoluteFile());
        Log.e(TAG, "FilesDir        =" + getFilesDir().getAbsoluteFile());
        Log.e(TAG, "ExternalFilesDir=" + getExternalFilesDir(null).getAbsoluteFile());
        getEncoderListByMimeType(MediaFormat.MIMETYPE_AUDIO_AAC);
        getCodecsListByMimeType(MediaFormat.MIMETYPE_AUDIO_AAC);
    }

    public void convertPcm2Wav() {
        String outputFolder = Objects.requireNonNull(getExternalFilesDir(null)).getAbsolutePath() + File.separator + "leo-audio";
        File file = new File(outputFolder, "original.pcm");
        File wavFile = new File(outputFolder, "original.wav");

        File apmFile = new File(outputFolder, "original-apm.pcm");
        File wavApmFile = new File(outputFolder, "original-apm.wav");

        try {
            PcmToWavUtil.pcmToWav(file, wavFile, 1, DEFAULT_SAMPLE_RATES, 16);
            PcmToWavUtil.pcmToWav(apmFile, wavApmFile, 1, DEFAULT_SAMPLE_RATES, 16);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void onRecordClick(View view) {
        mMicRecord = new MicRecord(DEFAULT_SAMPLE_RATES,
                DEFAULT_BIT_RATES,
                DEFAULT_CHANNEL_COUNT,
                CHANNEL_IN,
                DEFAULT_AUDIO_FORMAT);

        sendAudioDataToServer("START-AUDIO".getBytes(StandardCharsets.UTF_8));
//        sendAudioDataToServer(AUDIO_CSD_0);
        mMicRecord.doRecord(new Callback() {
            @Override
            public void onCallback(byte[] aacAudioData) {
                Log.w(TAG, "     Sending audio data[" + aacAudioData.length + "]: " + Arrays.toString(aacAudioData));
                sendAudioDataToServer(aacAudioData);
            }
        });

    }

    private void releaseAllResources() {
        if (mMicRecord != null) {
            mMicRecord.stop();
        }
        if (mAudioPlay != null) {
            mAudioPlay.stop();
        }
    }

    public void onStopClick(View view) {
        Log.i(TAG, "onStopClick");
        try {
            sendAudioDataToServer("END-OF-AUDIO".getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            e.printStackTrace();
        }
        releaseAllResources();
        convertPcm2Wav();
    }

    // ====================================================
    // ====================================================
    // ====================================================

    public void onPlayAacClick(View view) {
        mAudioPlay = new AudioPlay(DEFAULT_SAMPLE_RATES,
                CHANNEL_OUT,
                DEFAULT_AUDIO_FORMAT, AUDIO_CSD_0);
        mAudioPlay.stop();
        mAudioPlay.playAac();
    }

    public void onPlayPcmClick(View view) {
        mAudioPlay = new AudioPlay(DEFAULT_SAMPLE_RATES,
                CHANNEL_OUT,
                DEFAULT_AUDIO_FORMAT, AUDIO_CSD_0);
        mAudioPlay.stop();
        mAudioPlay.playPcm();
    }

    // ====================================================
    // ====================================================
    // ====================================================

    public void initTcpServer() {
        new Handler().post(() -> {
            try {
                TcpServer.run();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void onSendDataClick(View view) {
        sendAudioDataToServer("Hello World".getBytes(StandardCharsets.UTF_8));
    }

    private HandlerThread ht;
    private TcpClient client;

    public void sendAudioDataToServer(byte[] data) {
        if (client == null) {
            Log.e(TAG, "Client is null");
            return;
        }
//        Log.i(TAG, "Sending data 0: " + Arrays.toString(data));
        new Handler(ht.getLooper()).post(() -> {
            try {
                client.sendData(data);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void onStartServerClick(View view) {
        initTcpServer();
//        TcpServer.getHandler().initOutputStream();
    }

    public void onConnectServerClick(View view) {
        client = new TcpClient();
        String serverIp = ((EditText) findViewById(R.id.etSvrIp)).getText().toString();
        client.initClient(serverIp);

        ht = new HandlerThread("send-data");
        ht.start();
        Log.w(TAG, "Connected to server.");
    }

    // ====================================================
    // ====================================================
    // ====================================================

    public void onCaptureClick(View view) {
        startActivity(new Intent(this, CaptureImage.class));
    }


    public void getEncoderListByMimeType(String mimeType) {
        MediaCodecInfo[] codecInfos = new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos();
        for (MediaCodecInfo codecInfo : codecInfos) {
            if (codecInfo.isEncoder()) {
                for (String type : codecInfo.getSupportedTypes()) {
                    if (mimeType.equalsIgnoreCase(type)) {
                        Log.e(TAG, "type=" + type + " codecInfo=" + codecInfo.getName());
                    }
                }
            }
        }
    }

    public void getCodecsListByMimeType(String mimeType) {
        MediaCodecInfo[] codecInfos = new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos();
        for (MediaCodecInfo codecInfo : codecInfos) {
            for (String type : codecInfo.getSupportedTypes()) {
                if (mimeType.equalsIgnoreCase(type)) {
                    Log.e(TAG, "codecs type=" + type + " codecInfo=" + codecInfo.getName());
                }
            }
        }
    }

//    @Suppress("unused")
//    fun hasEncoderByCodecName(mimeType: String, codecName: String) =
//    getEncoderListByMimeType(mimeType).indexOfFirst { it.name.equals(codecName, true) } > -1

}
